package resource

import (
	"bytes"
	"context"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fcs-api/src/app/model"
	"fcs-api/src/app/utils"
	"fmt"
	"github.com/minio/minio-go/v7"
	"hash"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
)

var codePath, ossEndpoint, bucket string

func Upload(ak, sk string) error {
	// 和Dockerfile中的路径匹配
	codePath = "/usr/bin/fcs-api/runtime"
	ossEndpoint = model.Base.OssEndpoint
	bucket = model.Base.OssBucket
	if ak == "" || sk == "" {
		_ = logger.Warn("ak sk not ready")
		return errors.New("ak/sk not found")
	}
	location := "beijing"
	err := model.Base.MinioClient.MakeBucket(context.Background(), bucket, minio.MakeBucketOptions{Region: location})
	if err != nil {
		// Check to see if we already own this bucket (which happens if you run this twice)
		exists, errBucketExists := model.Base.MinioClient.BucketExists(context.Background(), bucket)
		if errBucketExists == nil && exists {
			fmt.Printf("We already own %s\n", bucket)
		} else {
			fmt.Println(err)
		}
	} else {
		fmt.Printf("Successfully created %s\n", bucket)
	}
	UploadDirFile(ak, sk, codePath)
	uploadTicket(ak, sk)
	logger.Info("-----code upload success-----")
	return nil
}

func isBucketExist() bool {
	ep := model.Base.OssAdminHost
	uri := "oss/v1/buckets/1/10?bucketName=" + bucket
	token := "bearer " + utils.GetAdminToken()
	body, err := utils.HttpReq(ep+uri, token, "GET", nil, nil)
	if err != nil {
		logger.Error("isBucketExist: Query ice oss bucket error %s", err.Error())
		return false
	}
	var e map[string]interface{}
	if err := json.Unmarshal(body, &e); err != nil {
		logger.Error("isBucketExist: Error parsing the response body: %s", err)
		return false
	}
	if e["totalCount"] != nil {
		totalCount := e["totalCount"].(float64)
		if totalCount > 0 {
			return true
		}
	}
	return false
}

func createBucket() {
	ep := model.Base.OssAdminHost
	uri := "oss/v1/buckets"
	requestBody := fmt.Sprintf("{\"bucketName\":\"%s\",\"acl\":\"0\",\"storageClass\":\"STANDARD\"}", bucket)
	token := "bearer " + utils.GetAdminToken()
	body, err := utils.HttpReq(ep+uri, token, "POST", bytes.NewReader([]byte(requestBody)), nil)
	if err != nil {
		logger.Error("createBucket: create ice oss bucket error %s", err.Error())
		logger.Error("createBucket: body is %s", string(body))
	}
}

func uploadTicket(ak, sk string) {
	b, err := ioutil.ReadFile("/usr/bin/fcs-api/ticket")
	if err != nil {
		_ = logger.Error("Read ticket err: %s", err)
		return
	}
	codeObjectName := "ticket"
	_, err = model.Base.MinioClient.PutObject(context.Background(), bucket, codeObjectName, strings.NewReader(string(b)), strings.NewReader(string(b)).Size(), minio.PutObjectOptions{ContentType: "application/octet-stream"})
	if err != nil {
		_ = logger.Error("uploadTicket err: %s", err)
		return
	}
}

func UploadDirFile(ak, sk, dirPth string) {
	runtimeFile, runtimeSubDir, err := GetFilesAndDirs(dirPth)
	if err != nil {
		_ = logger.Error("get runtimeCodes err: %s", err)
		return
	}
	for _, runtimeCode := range runtimeFile {
		b, err := ioutil.ReadFile(runtimeCode)
		if err != nil {
			_ = logger.Error("Read codeFile err: %s", err)
			continue
		}
		codeObjectName := "runtime" + strings.Replace(runtimeCode, codePath, "", -1)
		_, err = model.Base.MinioClient.PutObject(context.Background(), bucket, codeObjectName, strings.NewReader(string(b)), strings.NewReader(string(b)).Size(), minio.PutObjectOptions{ContentType: "application/octet-stream"})
		if err != nil {
			_ = logger.Error("ossUpload err: %s", err)
		}
	}
	if len(runtimeSubDir) > 0 {
		for _, subDir := range runtimeSubDir {
			go UploadDirFile(ak, sk, subDir)
		}
	}
}

func GetFilesAndDirs(dirPth string) (files []string, dirs []string, err error) {
	dir, err := ioutil.ReadDir(dirPth)
	if err != nil {
		return nil, nil, err
	}

	PthSep := string(os.PathSeparator)
	//suffix = strings.ToUpper(suffix) //忽略后缀匹配的大小写

	for _, fi := range dir {
		if fi.IsDir() { // 目录, 递归遍历
			dirs = append(dirs, dirPth+PthSep+fi.Name())
		} else {
			files = append(files, dirPth+PthSep+fi.Name())
		}
	}

	return files, dirs, nil
}

func ossUpload(url, date, token string, body io.Reader) error {

	r, err := http.NewRequest("PUT", url, body)
	if err != nil {
		_ = logger.Error("ossUploadOrCopy NewRequest error %v", err)
		return err
	}
	r.Header.Set("date", date)
	r.Header.Set("Authorization", token)

	resp, err := http.DefaultClient.Do(r)
	if err != nil {
		_ = logger.Error("ossUploadOrCopy Client.Do error %v", err)
		return err
	}
	if resp.StatusCode != 200 {
		body, _ := ioutil.ReadAll(resp.Body)
		_ = logger.Error("ossUploadOrCopy error %v : %v ", resp.StatusCode, string(body))
		return errors.New("oss upload err")
	}
	return nil
}

func getSignedStr(bucketName, objectName, contentType, date, ak, sk string) string {
	canonicalizedResource := fmt.Sprintf("/%s/%s", bucketName, objectName)

	canonicalizedOSSHeaders := ""

	signStr := "PUT" + "\n" + "" + "\n" + contentType + "\n" + date + "\n" + canonicalizedOSSHeaders + canonicalizedResource
	h := hmac.New(func() hash.Hash { return sha256.New() }, []byte(sk))
	_, _ = io.WriteString(h, signStr)
	signedStr := base64.StdEncoding.EncodeToString(h.Sum(nil))
	authorizationStr := "OSS " + ak + ":" + signedStr
	return authorizationStr
}
